package com.modules.config.interceptor;

import com.common.util.UserUtils;
import com.modules.upms.entity.SysUser;
import com.modules.upms.entity.vo.AuthUserVO;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.util.*;


/**
 * User : liulu
 * Date : 2017/12/1 11:30
 * version $Id: ParamInterceptor.java, v 0.1 Exp $
 */
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
}
)
public class ParamInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 拦截 Executor 的 update 方法 生成sql前将 tenantId 设置到实体中
        if (invocation.getTarget() instanceof Executor && invocation.getArgs().length == 2) {
            return invokeUpdate(invocation);
        }
        return invocation.proceed();
    }

    private Object invokeUpdate(Invocation invocation) throws Exception {
        Executor executor = (Executor) invocation.getTarget();
        // 获取第一个参数
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        // 非 insert 语句 不处理
        if (!ms.getSqlCommandType().equals(SqlCommandType.INSERT) && !ms.getSqlCommandType().equals(SqlCommandType.UPDATE) ) {
            return invocation.proceed();
        }
        // mybatis的参数对象
        Object paramObj = invocation.getArgs()[1];
        if (paramObj == null) {
            return invocation.proceed();
        }

        // 插入语句只传一个基本类型参数, 不做处理
        if (ClassUtils.isPrimitiveOrWrapper(paramObj.getClass())
                || String.class.isAssignableFrom(paramObj.getClass())
                || Number.class.isAssignableFrom(paramObj.getClass())) {
            return invocation.proceed();
        }

        AuthUserVO userDetails = UserUtils.getCurrentUser();
        if(userDetails == null) {
            SysUser user = new SysUser();
            user.setId(1L);
            userDetails = new AuthUserVO(user);
        }

        if (ms.getSqlCommandType().equals(SqlCommandType.INSERT)) {
            processCreateParam(paramObj, userDetails);
        }
        if (ms.getSqlCommandType().equals(SqlCommandType.UPDATE)) {
            processUpdateParam(paramObj, userDetails);
        }

        return executor.update(ms, paramObj);
    }

    private void processCreateParam(Object parameterObject, AuthUserVO userDetails) throws IllegalAccessException, InvocationTargetException {
        // 处理参数对象  如果是 map 且map的key 中没有 tenantId，添加到参数map中
        // 如果参数是bean，反射设置值
        Date nowDate = new Date();
        if (parameterObject instanceof Map) {
            ((Map) parameterObject).putIfAbsent("createDate", nowDate);
            ((Map) parameterObject).putIfAbsent("createUserId", userDetails.getUserId());
        } else {
            PropertyDescriptor ps = BeanUtils.getPropertyDescriptor(parameterObject.getClass(), "createDate");
            if (ps != null && ps.getReadMethod() != null && ps.getWriteMethod() != null) {
                Object value = ps.getReadMethod().invoke(parameterObject);
                if (value == null) {
                    ps.getWriteMethod().invoke(parameterObject, nowDate);
                }
            }
            PropertyDescriptor ps2 = BeanUtils.getPropertyDescriptor(parameterObject.getClass(), "createUserId");
            if (ps2 != null && ps2.getReadMethod() != null && ps2.getWriteMethod() != null) {
                Object value = ps2.getReadMethod().invoke(parameterObject);
                if (value == null) {
                    ps2.getWriteMethod().invoke(parameterObject, userDetails.getUserId());
                }
            }
        }
    }

    private void processUpdateParam(Object parameterObject, AuthUserVO userDetails) throws IllegalAccessException, InvocationTargetException {
        // 处理参数对象  如果是 map 且map的key 中没有 tenantId，添加到参数map中
        // 如果参数是bean，反射设置值
        Date nowDate = new Date();
        if (parameterObject instanceof Map) {
            ((Map) parameterObject).putIfAbsent("updateDate", nowDate);
            ((Map) parameterObject).putIfAbsent("updateUserId", userDetails.getUserId());
        } else {
            PropertyDescriptor ps = BeanUtils.getPropertyDescriptor(parameterObject.getClass(), "updateDate");
            if (ps != null && ps.getReadMethod() != null && ps.getWriteMethod() != null) {
                Object value = ps.getReadMethod().invoke(parameterObject);
                if (value == null) {
                    ps.getWriteMethod().invoke(parameterObject, nowDate);
                }
            }
            PropertyDescriptor ps2 = BeanUtils.getPropertyDescriptor(parameterObject.getClass(), "updateUserId");
            if (ps2 != null && ps2.getReadMethod() != null && ps2.getWriteMethod() != null) {
                Object value = ps2.getReadMethod().invoke(parameterObject);
                if (value == null) {
                    ps2.getWriteMethod().invoke(parameterObject, userDetails.getUserId());
                }
            }
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}